#ifndef CNC_BASE_MILL_DATABASE_IMPLEMENTATION_H
#define CNC_BASE_MILL_DATABASE_IMPLEMENTATION_H

// used interface
#include "persistentdata.h"
// provided interfaces
#include "taskctl.h"
#include "millpdata.h"
// include files
#include "workofs_.h"
#include "tooldata_.h"
#include "milldatamodel.h"
// basic component model
#include <bcm/bcm.h>
#include <sstream>

namespace cnc {

	namespace base {

		class MillDatabaseImpl : public bcm::Identifier<cnc::base::TaskControl>,
			public bcm::Identifier<MillPersistentData>
		{
		protected:
			bcm::GetComponentResult result_;
			PersistentData *pdata_;
			enum { NUM_WORK_OFFSETS = 7 };
			WorkOffsetImpl<3> workOfs_[NUM_WORK_OFFSETS];
			enum { NUM_TOOL_OFFSETS = 16 };
			ToolDataImpl toolOfs_[NUM_TOOL_OFFSETS];
		protected:
			MillDatabaseImpl() : bcm::Identifier<TaskControl>("taskctl", "Task Control"),
				bcm::Identifier<MillPersistentData>("millpdata", "Mill Persistent Data"),
				result_(bcm::GC_OK), pdata_(0)
			{
				bcm::ComponentManager &compMgr = bcm::ComponentManager::instance();
				switch (compMgr.loadComponent(TEXT("database.dll"))) {
				case bcm::LC_NODLL:
				case bcm::LC_NOCOMPONENT:
				case bcm::LC_ALREADYUSED:
					result_ = bcm::GC_INCOMPLETE;
					return;
				}
				bcm::Component *component;
				switch (compMgr.getComponent("db", &component)) {
				case bcm::GC_UNREGISTERED:
				case bcm::GC_NOCOMPONENT:
				case bcm::GC_INCOMPLETE:
					result_ = bcm::GC_INCOMPLETE;
					return;
				}
				bcm::Interface *iface;
				iface = component->getInterface("pdata");
				if (iface == 0) {
					result_ = bcm::GC_INCOMPLETE;
					return;
				}
				pdata_ = dynamic_cast<PersistentData *>(iface);
				if (pdata_ == 0) {
					result_ = bcm::GC_INCOMPLETE;
					return;
				}
			}
			cnc::base::TaskControl *getTaskControl()
			{
				return static_cast<cnc::base::TaskControl *>(this);
			}
			PersistentData *getPersistentData()
			{
				return pdata_;
			}
			MillPersistentData *getMillPersistentData()
			{
				return static_cast<MillPersistentData *>(this);
			}
		protected: // TaskControl
			virtual bool start()
			{
				if (load("mill.db") == false)
					reset();
				return true;
			}
			virtual bool execute()
			{
				return true;
			}
			virtual bool stop()
			{
				save("mill.db");
				return true;
			}
		protected: // MillPersistentData
			virtual bool load(const char *filename)
			{
				if (pdata_ == 0)
					return false;
				bool result = pdata_->load(filename);
				for (int i = 0; i < NUM_WORK_OFFSETS; i++) {
					for (int j = 0; j < 3; j++) {
						std::stringstream str;
						str << "path planning::work offset::index" << i << "::axis" << j;
						workOfs_[i].assign(j, pdata_->getParameter(str.str().c_str()));
					}
				}
				for (int i = 0; i < NUM_TOOL_OFFSETS; i++) {
					std::stringstream str_l;
					str_l << "path planning::tool offset::index" << i << "::length";
					toolOfs_[i].assignHeight(pdata_->getParameter(str_l.str().c_str()));
					std::stringstream str_d;
					str_d << "path planning::tool offset::index" << i << "::diameter";
					toolOfs_[i].assignDiameter(pdata_->getParameter(str_d.str().c_str()));
				}
				return result;
			}
			virtual bool save(const char *filename)
			{
				if (pdata_ == 0)
					return false;
				return pdata_->save(filename);
			}
			virtual void reset()
			{
				if (pdata_ == 0)
					return;
				pdata_->clear();
				Parameter *param;
				pdata_->addParameter("path planning::default metric system", DT_UINT8);
				param = pdata_->getParameter("path planning::default metric system");
				param->set(static_cast<uint8_t>(cnc::base::METRIC_SYSTEM::MS_INTERNATIONAL));
				pdata_->addParameter("path planning::default dimensioning mode", DT_INT8);
				param = pdata_->getParameter("path planning::default dimensioning mode");
				param->set(static_cast<int8_t>(cnc::base::DIMENSIONING_MODE::DM_ABSOLUTE));
				pdata_->addParameter("path planning::number of work offset", DT_UINT8);
				param = pdata_->getParameter("path planning::number of work offset");
				param->set(static_cast<uint8_t>(NUM_WORK_OFFSETS));
				for (int i = 0; i < NUM_WORK_OFFSETS; i++) {
					for (int j = 0; j < 3; j++) {
						std::stringstream str;
						str << "path planning::work offset::index" << i << "::axis" << j;
						pdata_->addParameter(str.str().c_str(), DT_INT32);
						workOfs_[i].assign(j, pdata_->getParameter(str.str().c_str()));
					}
				}
				/*pdata_->addParameter("path planning::maximum tool index", DT_UINT8);
				param = pdata_->getParameter("path planning::maximum tool index");*/
				pdata_->addParameter("atc::maximum tool index", DT_UINT8);
				param = pdata_->getParameter("atc::maximum tool index");
				param->set(static_cast<uint8_t>(NUM_TOOL_OFFSETS));
				for (int i = 0; i < NUM_TOOL_OFFSETS; i++) {
					std::stringstream str_l;
					str_l << "path planning::tool offset::index" << i << "::length";
					pdata_->addParameter(str_l.str().c_str(), DT_UINT32);
					toolOfs_[i].assignHeight(pdata_->getParameter(str_l.str().c_str()));
					std::stringstream str_d;
					str_d << "path planning::tool offset::index" << i << "::diameter";
					pdata_->addParameter(str_d.str().c_str(), DT_UINT32);
					toolOfs_[i].assignDiameter(pdata_->getParameter(str_d.str().c_str()));
				}
				pdata_->addParameter("axis motion::default cutting feedrate", DT_UINT32);
				param = pdata_->getParameter("axis motion::default cutting feedrate");
				param->set(static_cast<uint32_t>(0));
				pdata_->addParameter("axis motion::maximum cutting feedrate", DT_UINT32);
				param = pdata_->getParameter("axis motion::maximum cutting feedrate");
				param->set(static_cast<uint32_t>(0));
				pdata_->addParameter("axis motion::jog feedrate", DT_UINT32);
				param = pdata_->getParameter("axis motion::jog feedrate");
				param->set(static_cast<uint32_t>(0));
				pdata_->addParameter("axis motion::maximum jog feedrate", DT_UINT32);
				param = pdata_->getParameter("axis motion::maximum jog feedrate");
				param->set(static_cast<uint32_t>(200000000));
				pdata_->addParameter("axis motion::rapid traverse rate", DT_UINT32);
				param = pdata_->getParameter("axis motion::rapid traverse rate");
				param->set(static_cast<uint32_t>(0));
				pdata_->addParameter("axis motion::user rapid traverse override", DT_UINT8);
				param = pdata_->getParameter("axis motion::user rapid traverse override");
				param->set(static_cast<uint8_t>(0));
				pdata_->addParameter("axis motion::fast zero return traverse rate", DT_UINT32);
				param = pdata_->getParameter("axis motion::fast zero return traverse rate");
				param->set(static_cast<uint32_t>(0));
				pdata_->addParameter("axis motion::slow zero return traverse rate", DT_UINT32);
				param = pdata_->getParameter("axis motion::slow zero return traverse rate");
				param->set(static_cast<uint32_t>(0));
				pdata_->addParameter("axis motion::maximum traverse rate", DT_UINT32);
				param = pdata_->getParameter("axis motion::maximum traverse rate");
				param->set(static_cast<uint32_t>(0));
				pdata_->addParameter("axis motion::x axis motion travel range", DT_INT32);
				param = pdata_->getParameter("axis motion::x axis motion travel range");
				param->set(static_cast<int32_t>(0));
				pdata_->addParameter("axis motion::y axis motion travel range", DT_INT32);
				param = pdata_->getParameter("axis motion::y axis motion travel range");
				param->set(static_cast<int32_t>(0));
				pdata_->addParameter("axis motion::z axis motion travel range", DT_INT32);
				param = pdata_->getParameter("axis motion::z axis motion travel range");
				param->set(static_cast<int32_t>(0));
				pdata_->addParameter("axis motion::x axis reference point return direction", DT_UINT8);
				param = pdata_->getParameter("axis motion::x axis reference point return direction");
				param->set(static_cast<uint8_t>(cnc::base::AXES_REFERENCE_POINT_RETURN_DIRECTION::NEGATIVE));
				pdata_->addParameter("axis motion::y axis reference point return direction", DT_UINT8);
				param = pdata_->getParameter("axis motion::y axis reference point return direction");
				param->set(static_cast<uint8_t>(cnc::base::AXES_REFERENCE_POINT_RETURN_DIRECTION::NEGATIVE));
				pdata_->addParameter("axis motion::z axis reference point return direction", DT_UINT8);
				param = pdata_->getParameter("axis motion::z axis reference point return direction");
				param->set(static_cast<uint8_t>(cnc::base::AXES_REFERENCE_POINT_RETURN_DIRECTION::NEGATIVE));
				pdata_->addParameter("spindle::minimum spindle speed", DT_UINT16);
				param = pdata_->getParameter("spindle::minimum spindle speed");
				param->set(static_cast<uint16_t>(0));
				pdata_->addParameter("spindle::maximum spindle speed", DT_UINT16);
				param = pdata_->getParameter("spindle::maximum spindle speed");
				param->set(static_cast<uint16_t>(3000));
				pdata_->addParameter("spindle::maximum spindle output", DT_UINT32);
				param = pdata_->getParameter("spindle::maximum spindle output");
				param->set(static_cast<uint32_t>(0));
				pdata_->addParameter("spindle::minimum spindle speed override", DT_UINT8);
				param = pdata_->getParameter("spindle::minimum spindle speed override");
				param->set(static_cast<uint8_t>(50));
				pdata_->addParameter("spindle::maximum spindle speed override", DT_UINT8);
				param = pdata_->getParameter("spindle::maximum spindle speed override");
				param->set(static_cast<uint8_t>(120));
				pdata_->addParameter("atc::tool magazine capacity", DT_UINT8);
				param = pdata_->getParameter("atc::tool magazine capacity");
				param->set(static_cast<uint8_t>(16));
				/*pdata_->addParameter("atc::maximum tool index", DT_UINT8);
				param = pdata_->getParameter("atc::maximum tool index");
				param->set(static_cast<uint8_t>(0));*/
				pdata_->addParameter("atc::tool selection type", DT_UINT8);
				param = pdata_->getParameter("atc::tool selection type");
				param->set(static_cast<uint8_t>(cnc::base::TOOL_SELECTION_TYPE::FIXED));
				pdata_->addParameter("atc::selected tool index", DT_UINT8);
				param = pdata_->getParameter("atc::selected tool index");
				param->set(static_cast<uint8_t>(0));
				pdata_->addParameter("tool holder::maximum tool diameter", DT_UINT32);
				param = pdata_->getParameter("tool holder::maximum tool diameter");
				param->set(static_cast<uint32_t>(0));
				pdata_->addParameter("tool holder::maximum tool length", DT_UINT32);
				param = pdata_->getParameter("tool holder::maximum tool length");
				param->set(static_cast<uint32_t>(0));
				pdata_->addParameter("devices::number of devices", DT_UINT8);
				param = pdata_->getParameter("devices::number of devices");
				param->set(static_cast<uint8_t>(2));
			}
			virtual const char *getFirstParameterName()
			{
				if (pdata_ == 0)
					return 0;
				return pdata_->getFirstParameterName();
			}
			virtual const char *getNextParameterName()
			{
				if (pdata_ == 0)
					return 0;
				return pdata_->getNextParameterName();
			}
			virtual Parameter *getParameter(const char *paramID)
			{
				if (pdata_ == 0)
					return 0;
				return pdata_->getParameter(paramID);
			}
			virtual unsigned int getNumWorkOffsets() const
			{
				return NUM_WORK_OFFSETS;
			}
			virtual WorkOffset *getWorkOffset(const unsigned int index)
			{
				if (index >= NUM_WORK_OFFSETS)
					return 0;
				return &workOfs_[index];
			}
			virtual unsigned int getNumToolOffsets() const
			{
				return NUM_TOOL_OFFSETS;
			}
			virtual ToolData *getToolGeometry(const unsigned int index)
			{
				if (index >= NUM_TOOL_OFFSETS)
					return 0;
				return &toolOfs_[index];
			}
		};

	} // namespace base

} // namespace cnc

#endif
